package com.yfs.cloud.service.impl;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Objects;

import javax.persistence.Column;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.yfs.cloud.commons.Constants;
import com.yfs.cloud.commons.Like;
import com.yfs.cloud.commons.LikeType;
import com.yfs.cloud.commons.Sort;
import com.yfs.cloud.entity.BaseEntity;
import com.yfs.cloud.service.BaseService;
import com.yfs.cloud.util.ApplicationContextUtil;

import tk.mybatis.mapper.common.Mapper;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.entity.Example.Criteria;

/**
* @Company: 影法師工作室
* @Description: BaseService 的实现类
* @author 影法師  
* @date 2020年8月24日 下午4:15:05 
* @version V1.0
 */
@SuppressWarnings("all")
public abstract class BaseServiceImpl<T extends BaseEntity> implements BaseService<T> {

	/** 实体类是谁, 即 clazz = t.getClass(); */
	private Class<?> clazz;

	@Autowired
	private ApplicationContextUtil applicationContextUtil;

	public BaseServiceImpl() {
		// 带泛型参数的类型
		ParameterizedType parameterizedType = (ParameterizedType) this.getClass().getGenericSuperclass();
		// 得到参数类型数组(因为类型的泛型参数可能不止一个)
		Type[] types = parameterizedType.getActualTypeArguments();
		this.clazz = (Class<?>) types[0];
	}

	@Transactional
	@Override
	public T create(T t) {
		// 一般用 insertSelective 而不是用 insert,具体区别见
		// http://maven.apache.org/post/springboot-%E9%9B%86%E6%88%90mybatis/
		getMapper().insertSelective(t);
		return t;
	}

	@Transactional
	@Override
	public void remove(Serializable id) throws Exception {
		getMapper().deleteByPrimaryKey(id);
	}

	@Transactional
	@Override
	public void remove(T t) throws Exception {
		getMapper().delete(t);
	}

	@Transactional
	@Override
	public void remove(List<Serializable> ids) throws Exception {
		Example example = new Example(clazz);
		Criteria criteria = example.createCriteria();
		criteria.andIn("id", ids);
		getMapper().deleteByExample(example);
	}

	@Transactional
	@Override
	public void modify(T t) {
		getMapper().updateByPrimaryKeySelective(t);
	}

	@Override
	public T findOne(T t) {
		return (T) getMapper().selectOne(t);
	}

	@Override
	public List<T> findList(T t, Sort[] sorts, Like[] likes) throws Exception {
		StringBuilder orderByClause = new StringBuilder();
		// 如果不是模糊查询
		if (Objects.isNull(likes)) {
			// 如果不是排序查询的话
			if (Objects.isNull(sorts)) {
				return getMapper().select(t);
				// 如果是排序查询的话
			} else {
				Example example = new Example(t.getClass());
				Criteria criteria = example.createCriteria();
				// 创建 andEqualTo
				createSql(criteria, t);
				// 创建 sort
				for (int i = 0; i < sorts.length; i++) {
					if (i != 0) {
						orderByClause.append(Constants.COMMA);
					}
					orderByClause.append(StringUtils.SPACE + getColumnName(sorts[i].getSortPropertyName())
							+ StringUtils.SPACE + sorts[i].getSortType());
				}
				example.setOrderByClause(orderByClause.toString());
				return getMapper().selectByExample(example);
			}
			// 如果是模糊查询的话
		} else {
			Example example = new Example(t.getClass());
			Criteria criteria = example.createCriteria();
			// 创建 like 模糊查询
			for (int i = 0; i < likes.length; i++) {
				// 如果是与
				if (likes[i].getOrAnd() == LikeType.AND) {
					criteria.andLike(likes[i].getLikePropertyName(),
							Constants.PERCENT + likes[i].getLikePropertyValue() + Constants.PERCENT);
					// 如果是或
				} else {
					criteria.orLike(likes[i].getLikePropertyName(),
							Constants.PERCENT + likes[i].getLikePropertyValue() + Constants.PERCENT);
				}
			}
			// 如果不是排序查询的话
			if (Objects.isNull(sorts)) {
				return getMapper().selectByExample(example);
				// 如果是排序查询的话
			} else {
				// 创建 andEqualTo
				createSql(criteria, t);
				// 创建 sort
				for (int i = 0; i < sorts.length; i++) {
					if (i != 0) {
						orderByClause.append(",");
					}
					orderByClause.append(StringUtils.SPACE + getColumnName(sorts[i].getSortPropertyName())
							+ StringUtils.SPACE + sorts[i].getSortType());
				}
				example.setOrderByClause(orderByClause.toString());
				return getMapper().selectByExample(example);
			}
		}
	}

	@Override
	public PageInfo<T> findPage(T t, int pageNum, int pageSize, Sort[] sorts, Like[] likes) throws Exception {
		Assert.isTrue(pageNum > 0, Constants.PAGE_NUM_INVALID);
		Assert.isTrue(pageSize > 0, Constants.PAGE_SIZE_INVALID);
		// mybatis 分页插件
		PageHelper.startPage(pageNum, pageSize);
		List<T> list = this.findList(t, sorts, likes);
		PageInfo<T> pageInfo = new PageInfo<>(list);
		return pageInfo;
	}

	/**
	 * 
	* @Title: getMapper 
	* @Description: 获取 mapper 
	* @param @return    
	* @return Mapper    
	* @throws
	 */
	private Mapper getMapper() {
		// UserNameMapper 在 ioc 容器中的 id 是 userNameMapper, 即首字母小写再加上Mapper
		return (Mapper) applicationContextUtil.getBean(StringUtils.uncapitalize(this.clazz.getSimpleName()) + "Mapper");
	}

	/**
	* @Description: 获取 实体类  propertyName 属性对应的数据库字段名称
	* @author 影法師 
	* @date 2020年8月19日 下午2:06:27 
	* @version V1.0 
	* @param @param propertyName
	* @param @return    
	* @return String    
	* @throws
	 */
	private String getColumnName(String propertyName) throws NoSuchFieldException, SecurityException {
		Field field = null;
		// 获取名为 propertyName 属性
		try {
			field = clazz.getDeclaredField(propertyName);
		} catch (NoSuchFieldException | SecurityException e) {
			// 说明该属性是 BaseEntity 中的属性
			field = BaseEntity.class.getDeclaredField(propertyName);
		}
		// 获取该属性上的 @Column 注解
		Column column = field.getAnnotation(Column.class);
		// 如果没有写 @Column 注解的话, 例如 sex 这种, 则属性名和数据库字段名一样. 则直接返回 propertyName
		if (Objects.isNull(column)) {
			return propertyName;
		} else {
			return column.name();
		}
	}

	private void createSql(Criteria criteria, T t) throws Exception {
		// 获取 BaseEntity 中的属性
		Field[] baseFields = BaseEntity.class.getDeclaredFields();
		for (Field field : baseFields) {
			createSql(criteria, t, field);
		}
		// 获取 本身的属性
		Field[] clazzFields = clazz.getDeclaredFields();
		for (Field field : clazzFields) {
			createSql(criteria, t, field);
		}
	}

	private void createSql(Criteria criteria, T t, Field field) throws Exception {
		String fieldName = field.getName();
		String getterName = "get" + StringUtils.capitalize(fieldName);
		Method getterMethod = null;
		try {
			getterMethod = clazz.getDeclaredMethod(getterName);
		} catch (Exception e) {
			// 说明是 BaseEntity 中的属性
			getterMethod = BaseEntity.class.getDeclaredMethod(getterName);
		}
		Object propertyValue = getterMethod.invoke(t);
		// 如果填充了属性, 则才更新 sql
		if (!Objects.isNull(propertyValue)) {
			criteria.andEqualTo(fieldName, propertyValue);
		}
	}

}
